package attributes;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.Map.Entry;

import trees.AttributeLeaf;
import trees.AttributeNode;
import trees.AttributeTree;
import trees.TreePrinter;
import utils.TwoWayHashMap;

import au.com.bytecode.opencsv.CSVReader;

public class HierarchicalAttributesImporter {
	
	public static AttributeGraphPlusDictionary getChildParentPairsFromFile(String path){

		List<String[]> entryList = null;
		try{
			CSVReader reader = new CSVReader(
					new FileReader(
							path));
			
			try{
				
				//reader.readNext();
			    entryList = reader.readAll();
			    System.out.println("number of lines to be processed: " + entryList.size());
			} catch(IOException ioe){
				System.out.println("" + ioe);
				System.exit(0);				
			}
		} catch(FileNotFoundException fnfe){
			System.out.println("" + fnfe);
			System.exit(0);
		}		
		System.out.println("processing attributes for file: " + path);
		return getChildParentPairsFromList(entryList);
	}
	
	public static AttributeGraphPlusDictionary getChildParentPairsFromString(String string){
		List<String[]> entryList = null;
		CSVReader reader = new CSVReader(
				new StringReader(
						string));
		
		try{
			
			//reader.readNext();
		    entryList = reader.readAll();
		    System.out.println("number of lines to be processed: " + entryList.size());
		} catch(IOException ioe){
			System.out.println("" + ioe);
			System.exit(0);				
		}
		
		return getChildParentPairsFromList(entryList);
	}	
	
	public static AttributeGraphPlusDictionary getChildParentPairs(String path){
		return getChildParentPairsFromFile(path);
	}	
		
	public static AttributeGraphPlusDictionary getChildParentPairsFromList(List<String[]> entryList){
		
		//use a two-way hashmap to store the names of the attributes
		TwoWayHashMap<String, Integer> attributeIndices = new TwoWayHashMap<String, Integer>();
		int attributeIndexCounter = 0;
		
		//first collect the list of unique attribute strings
	    for (String [] nextLine : entryList) {				
		    //while ((nextLine = reader.readNext()) != null) {
		    for(int i = 0; i < nextLine.length; i++){
		    	//get the next token; trim it of surrounding whitespace
		    	String inputToken = nextLine[i].trim();
		    	//see if that token has already been seen
		    	Integer inputIndex = attributeIndices.getForward(inputToken);
		    	if(inputIndex == null) {
		    		//if it hasn't, register it with the current token index
		    		//and update the token index
		    		attributeIndices.put(inputToken, attributeIndexCounter);
		    		attributeIndexCounter++;
		    	}
		    }
		}	
	    List<String> leafNames = new ArrayList<String>();
	    HashMap<Integer, Integer> childParentPairs = new HashMap<Integer, Integer>();
		
	    //get the leaves from the first line of the file
	    //the implementation detail that the leaves use the first
	    //indices is used elsewhere in the application, namely
	    //in the fact that only the leaves are real attributes and the non-leaf
	    //nodes are "erased" after combination computations take place
	    String [] firstLine = entryList.get(0);
	    for(int i = 0; i < firstLine.length; i++){
	    	leafNames.add(i, firstLine[i].trim());
	    	System.out.print(firstLine[i].trim() + ";");
	    }
	    //System.out.println("");
	    
	    //now that we have processed the first line 
	    //we can remove it;  also, unlike the following lines, the
	    //first line does not represent parent-child relationships
	    //and thus should not be processed like the following ones
	    entryList.remove(0);
	    
	    for (String [] nextLine : entryList) {				
		    //while ((nextLine = reader.readNext()) != null) {
	    	
	    	//the first token is the parent; if there is no parent
	    	//then there is no info to be gotten from the line
	    	if(nextLine[0] == null) continue;
	    	Integer parent = attributeIndices.getForward(nextLine[0].trim());
	    	
	    	//store all the child-parent pairs in the hashmap
	    	//on a given line first comes the parent, then its children
		    for(int i = 1; i < nextLine.length; i++){
		    	Integer child = attributeIndices.getForward(nextLine[i].trim());
		    	childParentPairs.put(child, parent);
		    }
		}
	    //now we are done processing the file and just need to process
	    //the data in the hashmaps
	    
	    //find the roots of the forest
	    //first assume all nodes(tokens) are roots by adding them all to a list
	    //it just so happens the node(token) indices are the consecutive integers
	    //from zero up to the number if nodes minus 1
	    HashMap<Integer, Object> parentlessNodes = new HashMap<Integer, Object>();
	    HashMap<Integer, Object> childlessNodes = new HashMap<Integer, Object>();
	    for(int i = 0; i < attributeIndexCounter; i++){
	    	parentlessNodes.put(i, new Object());
	    	childlessNodes.put(i, new Object());
	    	//System.out.println("parentless node: " + i + " : " + attributeIndices.getBackwardMap().get(i));
	    }
	    //then remove ones with children from the list
		for(Entry<Integer, Integer> entry : childParentPairs.entrySet()){
			Integer childIndex = entry.getKey();
			Integer parentIndex = entry.getValue();
			//parentlessNodes.put(childIndex, null);
			parentlessNodes.remove(childIndex);
			childlessNodes.remove(parentIndex);
			//System.out.println("removing: " + attributeIndices.getBackwardMap().get(childIndex));
		}
		
		//convert the hashmap to a list of its key values
		ArrayList<Integer> rootIndices = new ArrayList<Integer>();
		Set<Integer> parentlessIndices = parentlessNodes.keySet();
		for(Integer index : parentlessIndices){
			rootIndices.add(index);
			//System.out.println("adding root: " + attributeIndices.getBackwardMap().get(index));
		}
		
		HashMap<Integer, String> dictionary = attributeIndices.getBackwardMap();
		
		//create real nodes for each of the tokens
		HashMap<Integer, AttributeNode> nodes = new HashMap<Integer, AttributeNode>();
		for(int i = 0; i < attributeIndexCounter; i++){
			if(childlessNodes.get(i) == null){
				nodes.put(i, new AttributeTree(i));
				//System.out.println("added " + dictionary.get(i) + " as a tree");
			}
			else{
				nodes.put(i, new AttributeLeaf(i));
				//System.out.println("added " + dictionary.get(i) + " as a leaf");
			}
		}
		
		//go through all the nodes and add parent-child relationships
		for(Entry<Integer, Integer> entry : childParentPairs.entrySet()){
			Integer childIndex = entry.getKey();
			Integer parentIndex = entry.getValue();
			AttributeNode childNode = nodes.get(childIndex);
			AttributeNode parentNode = nodes.get(parentIndex);
			parentNode.getChildren().add(childNode);
			//System.out.println("added " + dictionary.get(childIndex) +
			//		" as a child of " + dictionary.get(parentIndex));
		}
		
		/*
		TreePrinter treePrinter = new TreePrinter(dictionary);
		for(Integer rootIndex : rootIndices){
			AttributeNode tree = nodes.get(rootIndex);
			treePrinter.printTree(tree);
		}
		*/		
		
		 // @param childParentPairs a list of child parent pairs by node index
		 // @param dictionary a map from node indices to strings
		 // @param leaves a list of the names of the strings
		 // @param roots a list of the indices of the roots
	    AttributeGraphPlusDictionary attributeGraphPlusDictionary =
	    	new AttributeGraphPlusDictionary(
	    			childParentPairs,
	    			dictionary,
	    			leafNames,
	    			rootIndices,
	    			nodes
	    	);
	    
		return attributeGraphPlusDictionary;
	    
	}
	
}


